/* fmpeg4_avcodec.c
   It is sample code for ffmpeg application usage, to define AVCodec structure.
   Note it's not a part of fmpeg driver, but collect int the same folder
 */
#include "../../../libavcodec/avcodec.h"
#include <sys/ioctl.h>
#include <sys/signal.h>
#include <sys/mman.h>
#include <sys/resource.h> 
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h> 
#include <assert.h>
#include <fcntl.h>
#include <unistd.h> 
#include <string.h> 
#include "fmpeg4_avcodec.h"
#include "ratecontrol.h"

//#define DEBUG_IN_FILE
//#define DEBUG_OUT_FILE
//#define USE_MMAP
//#define SHOW_PERFORMANCE_EVALUATION_RESULT
#define CUSTOMER_QUANT_TABLE

//////////////// PERFORMANCE REPORT ////////////////
#ifdef SHOW_PERFORMANCE_EVALUATION_RESULT
#include <sys/time.h>
typedef unsigned long long uint64;

static void time_start(struct timeval *tv)
{	
      gettimeofday (tv, NULL);
}

static uint64 time_delta(struct timeval *start, struct timeval *stop)
{
      uint64 secs, usecs;

      secs = stop->tv_sec - start->tv_sec;
      usecs = stop->tv_usec - start->tv_usec;
      if (usecs < 0) {
        secs--;
        usecs += 1000000;
      }
      return secs * 1000000 + usecs;
}

static uint64 time_stop(struct timeval *begin)
{
      struct timeval end;
      gettimeofday (&end, NULL);
      return time_delta (begin, &end);
}
#endif
//////////////// PERFORMANCE REPORT ////////////////

extern int fmjpeg_encoder_sj(int width,int height,char *data0,char *data1,char *data2, char *outfile);
void fmpeg4_get_mb_info(MACROBLOCK_INFO *mbinfo);
void fmpeg4_get_mb_flag(int *flag);

#define FMPEG4_DECODER_DEV  "/dev/fdec"
#define FMPEG4_ENCODER_DEV  "/dev/fenc"

int fmpeg4_dec_fd=0;
int fmpeg4_enc_fd=0;

#ifdef USE_MMAP
static int dec_mmap_addr,enc_mmap_addr;
#endif

#ifdef DEBUG_OUT_FILE
char dout_name[30];
FILE *dout;
#endif

#ifdef DEBUG_IN_FILE
char din_name[30];
FILE *din;
#endif

typedef struct FMpeg4DecContext {
    #ifdef SHOW_PERFORMANCE_EVALUATION_RESULT
    unsigned int   pic_count;
    uint64              nsecs;
    struct               timeval t1;
    #endif    
} FMpeg4DecContext;

int xvid_decoder_init(AVCodecContext *avctx)
{
    FMP4_DEC_PARAM  tDecParam;
    FMpeg4DecContext       *s = avctx->priv_data;	
	
    avctx->pix_fmt= PIX_FMT_RGB555;
    avctx->flags |= CODEC_FLAG_PICT_ALLOC_EXTERNAL;

    if(fmpeg4_dec_fd==0)
	    fmpeg4_dec_fd=open(FMPEG4_DECODER_DEV,O_RDWR);
	    
    if(fmpeg4_dec_fd<=0) {
	    printf("Fail to open %s\n",FMPEG4_DECODER_DEV);
	    return -1;
    }

#ifdef DEBUG_OUT_FILE
    sprintf(dout_name,"/tmp/out%d.yuv",getpid());
    dout=fopen(dout_name,"wb");
    printf("Use decoder output name %s\n",dout_name);
#endif

#ifdef DEBUG_IN_FILE
    sprintf(din_name,"/tmp/in%d.m4v",getpid());
    din=fopen(din_name,"wb");
    printf("Use decoder input name %s\n",din_name);
#endif

#ifdef USE_MMAP
    dec_mmap_addr=(int)mmap(0,(avctx->framebuffer_rgb_stride*avctx->framebuffer_rgb_height*3)/2,PROT_READ|PROT_WRITE,MAP_SHARED,fmpeg4_dec_fd, 0);
    if(dec_mmap_addr<=0)
        return -1;
    printf("mmap_addr=0x%x\n",dec_mmap_addr);
#endif

    tDecParam.u32API_version = 0x00020002;
    tDecParam.u32MaxWidth = avctx->width;
    tDecParam.u32MaxHeight = avctx->height;
    tDecParam.u32FrameWidth = avctx->framebuffer_rgb_stride;
    tDecParam.u32FrameHeight = avctx->framebuffer_rgb_height;

    if ( ioctl(fmpeg4_dec_fd,FMPEG4_IOCTL_DECODE_INIT,&tDecParam) < 0 ) {
    	close(fmpeg4_dec_fd);
         printf("Error to set FMPEG4_IOCTL_DECODE_INIT\n");
	return -1;
    }
	
    #ifdef SHOW_PERFORMANCE_EVALUATION_RESULT
    s->pic_count=0;	
    time_start(&(s->t1));
    #endif 	
    return 0;
}

int xvid_decoder_end(AVCodecContext *avctx)
{
    FMpeg4DecContext       *s = avctx->priv_data;	
	
    if(fmpeg4_dec_fd)     {
        #ifdef USE_MMAP
        munmap(dec_mmap_addr,(avctx->framebuffer_rgb_stride*avctx->framebuffer_rgb_height*3)/2);
        #endif
        close(fmpeg4_dec_fd);
    }
    fmpeg4_dec_fd=0;

#ifdef DEBUG_OUT_FILE
    fclose(dout);
#endif
	
#ifdef DEBUG_IN_FILE
    fclose(din);
#endif

#ifdef SHOW_PERFORMANCE_EVALUATION_RESULT	
    s->nsecs = time_stop(&(s->t1)) / 1000;
    printf("spend %lld milliseconds with toal frame count %d = %f(frames/per second)\n", s->nsecs,s->pic_count,(float)((float)s->pic_count)/((float)s->nsecs/(float)1000.0));
    printf("MP4 decoding is done. %d frames are encoded\n",s->pic_count);	
#endif		
    return 0;
}

int xvid_decode(AVCodecContext *avctx, void *data, int *got_picture, uint8_t *buf, int buf_size)
{
    FMpeg4DecContext       *s = avctx->priv_data;	
    AVFrame         *picture=(AVFrame *)data;
    fmpeg4_parm_t   parm;

    parm.input_va_y=(unsigned int)buf;                     //input bit stream
    parm.output_va_y=(unsigned int)picture->data[0];   //output phy address Y or RGB
    parm.output_va_u=(unsigned int)picture->data[1];   //output phy address U
    parm.output_va_v=(unsigned int)picture->data[2];   //output phy address V
    parm.length=buf_size;
    //printf("xvid_decode input=0x%x output va=0x%x 0x%x 0x%x\n",parm.input_va_y,parm.output_va_y,parm.output_va_u,parm.output_va_v);

    #ifdef DEBUG_IN_FILE
    fwrite((void *)parm.input_va_y,buf_size,1,din);
    #endif

#if 0 //shift(x,y)
{ 
    unsigned int x,y;
    y=0; //shift y pixel
    x=3; //shift 16*x pixel
    parm.output_va_y+=((720*y)+(16*x));
    parm.output_va_u+=((360*y/2)+(8*x));
    parm.output_va_v+=((360*y/2)+(8*x));
}
#endif

    if ( ioctl(fmpeg4_dec_fd,FMPEG4_IOCTL_DECODE_FRAME,&parm) < 0 ) {
         close(fmpeg4_dec_fd);
         printf("Error to set FMPEG4_IOCTL_DECODE_FRAME\n");
	return -1;
    }
	
    *got_picture=parm.got_picture;
    //getchar();
    
    //ivan
    #ifdef DEBUG_OUT_FILE
    fwrite((void *)parm.output_va_y,avctx->width*avctx->height,1,dout);
    fwrite((void *)parm.output_va_u,avctx->width*avctx->height/4,1,dout);
    fwrite((void *)parm.output_va_v,avctx->width*avctx->height/4,1,dout);
    #endif

    if(*got_picture==0)   {
        printf("Bad MPEG4 Decoding!\n");
        return -1;
    }
	
    #ifdef SHOW_PERFORMANCE_EVALUATION_RESULT
    s->pic_count++;
    #endif
	
    return parm.length;
}


/******************************************************************************
    Faraday MPEG4 encoder
 *****************************************************************************/
typedef struct FMpeg4EncContext {
    AVCodecContext *avctx;
    MACROBLOCK_INFO *mdinfo;	
    MOTION_INFO *motion;	
    int roi_enable;	
    void *enc_handle;
    #ifdef SHOW_PERFORMANCE_EVALUATION_RESULT
    unsigned int   pic_count;
    uint64              nsecs;
    struct               timeval t1;
    #endif    	
} FMpeg4EncContext;

#ifdef CUSTOMER_QUANT_TABLE
FILE *qin;
unsigned char qin_name[30];
unsigned char *cust_inter_qtbl;
unsigned char *cust_intra_qtbl;
#endif

RateControl ratec;
struct mcfg {
    unsigned int    exist;
    pid_t           pid;
    unsigned int    md_interval;
    unsigned int    x0_LU;
    unsigned int    y0_LU;
    unsigned int    x0_RD;
    unsigned int    y0_RD;
    
    unsigned int    x1_LU;
    unsigned int    y1_LU;
    unsigned int    x1_RD;
    unsigned int    y1_RD;
    
    unsigned int    x2_LU;
    unsigned int    y2_LU;
    unsigned int    x2_RD;
    unsigned int    y2_RD;

    unsigned int    mv_th0;
    unsigned int    sad_th0;
    unsigned int    dev_th0;
    unsigned int    alarm_idx0;

    unsigned int    mv_th1;
    unsigned int    sad_th1;
    unsigned int    dev_th1;
    unsigned int    alarm_idx1;

    unsigned int    mv_th2;
    unsigned int    sad_th2;
    unsigned int    dev_th2;
    unsigned int    alarm_idx2;
    
    unsigned char   filename[30];
    unsigned int    rotate;
    unsigned int    rotate_idx;
} mcfg;

struct mcfg mval;
unsigned int frm_count=0;
#define WORK_FRMS  30
void xvid_set_motion_cfg(struct mcfg *val)
{
    memcpy(&mval,val,sizeof(struct mcfg));
}

void motion_info_init(MOTION_INFO *motion)
{
	motion->range_mb_x0_LU = mval.x0_LU;
  	motion->range_mb_y0_LU = mval.y0_LU;
  	motion->range_mb_x0_RD = mval.x0_RD;
  	motion->range_mb_y0_RD = mval.y0_RD;
  	motion->range_mb_x1_LU = mval.x1_LU;
  	motion->range_mb_y1_LU = mval.y1_LU;
  	motion->range_mb_x1_RD = mval.x1_RD;
  	motion->range_mb_y1_RD = mval.y1_RD;
  	motion->range_mb_x2_LU = mval.x2_LU;
  	motion->range_mb_y2_LU = mval.y2_LU;
  	motion->range_mb_x2_RD = mval.x2_RD;
  	motion->range_mb_y2_RD = mval.y2_RD;

  	motion->MV_th0 = mval.mv_th0;    
  	motion->sad_th0 = mval.sad_th0;   
  	motion->delta_dev_th0 = mval.dev_th0;   
                                     
  	motion->MV_th1 = mval.mv_th1;        
  	motion->sad_th1 = mval.sad_th1;       
  	motion->delta_dev_th1 = mval.dev_th1;       

  	motion->MV_th2 = mval.mv_th2;      
  	motion->sad_th2 = mval.sad_th2;     
  	motion->delta_dev_th2 = mval.dev_th2;     

  	motion->md_interval = mval.md_interval;  	
  	motion->motion_dection_enable=mval.exist;

}	

void do_Static_JPEG(char *filename, char *dataY, char *dataU, char *dataV, int w, int h) 
{
	char oname[30];
    	char wi[10],hi[10],quant[10];
    	int len;
    
    	sprintf(wi,"%d",w);
    	sprintf(hi,"%d",h);
    	sprintf(quant,"256");
    	strcpy(oname,filename);
    	len=strlen(oname);
    	oname[len-1]='g';
    	oname[len-2]='p';
    	oname[len-3]='j';
	//printf("filename:%s\n",filename);
	//printf("do_Static_JPEG:%s\n",oname);
	if ( fmjpeg_encoder_sj( w, h, dataY, dataU, dataV, oname) < 0 )
		 printf("error to encode\n");

}


void do_JPEG(char *filename,int w,int h)
{
    char oname[30];
    char wi[10],hi[10],quant[10];
    int len;
    
    sprintf(wi,"%d",w);
    sprintf(hi,"%d",h);
    sprintf(quant,"256");
    strcpy(oname,filename);
    len=strlen(oname);
    oname[len-1]='g';
    oname[len-2]='p';
    oname[len-3]='j';
    
    if(execlp("yuv2jpg","yuv2jpg",filename,oname,wi,hi,quant,(char *)0)<0)
	    printf("error to execle\n");
}

//void do_alarm(char *filename,pid_t alarm_pid,int w,int h)
void do_alarm(char *filename,pid_t alarm_pid,int w,int h,char *data0,char *data1,char *data2)
{
    pid_t gpid;

    if((gpid=fork())<0)
        printf("fork error");
    else if(gpid==0)
    {
        //do_JPEG(filename,w,h);
        do_Static_JPEG(filename,data0,data1,data2,w,h);
        exit(0);
    }

    if(waitpid(gpid,NULL,0)<0)
        printf("wait error");

    remove(filename);
    if(alarm_pid!=0)
    {
        kill(alarm_pid,SIGALRM);
        //printf("Send signal SIGALRM to process %d\n",alarm_pid);
    }
}
#if 0
void motion_alarm_action(Faraday_ENC_FRAME *enc_frame, MOTION_INFO   *motion, int width,int height,char *data0,char *data1,char *data2)
{
//    FILE            *fd;
    unsigned char   filename[30];
//    int             ysize,uvsize;
    pid_t           gpid;

    frm_count++;
	
    //if((enc_frame->frameindex==0)&&(frm_count>WORK_FRMS))		
    if( (frm_count % WORK_FRMS )==1)				
    {
	//    if((enc_frame->active0>mval.alarm_idx0)||(enc_frame->active1>mval.alarm_idx1)||(enc_frame->active2>mval.alarm_idx2))
	    {
	        printf("\n<Motion Dection Alarm (%d,%d,%d)!>\n",enc_frame->active0,enc_frame->active1,enc_frame->active2);
	        frm_count=0;
	        sprintf(filename,"%s%02d.yuv",mval.filename,mval.rotate_idx);
			
	        if(mval.rotate_idx>=mval.rotate)
	            mval.rotate_idx=1;
	        else
	            mval.rotate_idx++;
			
            if((gpid=fork())<0)
                printf("fork error");
	   else if(gpid==0)
            {
                //do_alarm(filename,mval.pid,width,height);
                do_alarm(filename,mval.pid,width,height,data0,data1,data2);
                exit(0);
            }
	   if(waitpid(gpid,NULL,0)<0)
	        printf("wait error");
        }
    }
}
#else
void motion_alarm_action(Faraday_ENC_FRAME *enc_frame, MOTION_INFO   *motion, int width,int height,char *data0,char *data1,char *data2)
{
    unsigned char   filename[30];
    pid_t           gpid;

    frm_count++;
	
    if(((enc_frame->frameindex%mval.md_interval)==0)&&(frm_count>WORK_FRMS))				
    {
	    if((enc_frame->active0>mval.alarm_idx0)||(enc_frame->active1>mval.alarm_idx1)||(enc_frame->active2>mval.alarm_idx2))
	    {
	        printf("\n<Motion Dection Alarm (%d,%d,%d)!>\n",enc_frame->active0,enc_frame->active1,enc_frame->active2);
	        frm_count=0;
	        sprintf(filename,"%s%02d.yuv",mval.filename,mval.rotate_idx);
			
	        if(mval.rotate_idx>=mval.rotate)
	            mval.rotate_idx=1;
	        else
	            mval.rotate_idx++;
#if 0
	        remove(filename);
	        fd=fopen(filename,"wb");
	        if(fd<=0)
	            return;

            ysize=width*height;
            uvsize=width*height/4;

            /* write Y */
            fwrite(data0,1,ysize,fd);

            /* write U */
            fwrite(data1,1,uvsize,fd);

            /* write V */
            fwrite(data2,1,uvsize,fd);
            fclose(fd);
#endif
            if((gpid=fork())<0)
                printf("fork error");
	   else if(gpid==0)
            {
                //do_alarm(filename,mval.pid,width,height);
                do_alarm(filename,mval.pid,width,height,data0,data1,data2);
                exit(0);
            }
	   if(waitpid(gpid,NULL,0)<0)
	        printf("wait error");
        }
    }
}
#endif
#ifdef CUSTOMER_QUANT_TABLE
int fmpeg4_get_quant_tbls(unsigned char *inter_qtbl, unsigned char *intra_qtbl)
{
    int item=0;
    unsigned char buf;
    unsigned char num;	
    
    sprintf(qin_name,"/ffmpeg/quant.txt");
    qin=fopen(qin_name,"r");
    if( !qin ) {
	printf("open %s fail\n",qin_name);
	return 0;
    }
	
    printf("Use quant tbl input name %s\n",qin_name);

    while( !feof(qin) ) {
    	
        fread(&buf, 1, sizeof(unsigned char), qin);

	if ( (buf !=0x20) && (buf >=0x30) && (buf<=0x39) )	{
		num = buf-0x30;
		do {
			fread(&buf, 1, sizeof(unsigned char), qin);
			if ( buf != 0x20)
				num = num*10 + (buf-0x30);
		} while( buf != 0x20 ) ;
        
        		if((item>=0) && (item<64) ){		
			inter_qtbl[item] = num;
			item++;
        		} else if ((item>=64) && (item<128) ){
                  	intra_qtbl[item-64] = num;
			item++;	
        		} else  { 
            		printf("customer Quant Tbl number err\n");
        		}
	}
    }	
  
    fclose(qin);	
    if ( item<127) {
	printf("the number of quant tbl's element err\n");	
	return 0;
    }
    return 1;	
}

int fmpeg4_get_inter_quant_tbls(unsigned char *inter_qtbl)
{
    int item=0;
    unsigned char buf;
    unsigned char num;	
    
    sprintf(qin_name,"/ffmpeg/inter_quant.txt");
    qin=fopen(qin_name,"r");
    if( !qin ) {
	printf("open %s fail\n",qin_name);
	return 0;
    }
	
    printf("Use inter quant tbl input name %s\n",qin_name);

    while( !feof(qin) ) {
    	
        fread(&buf, 1, sizeof(unsigned char), qin);

	if ( (buf !=0x20) && (buf >=0x30) && (buf<=0x39) )	{
		num = buf-0x30;
		do {
			fread(&buf, 1, sizeof(unsigned char), qin);
			if ( buf != 0x20)
				num = num*10 + (buf-0x30);
		} while( buf != 0x20 ) ;
        
        		if((item>=0) && (item<64) ){		
			inter_qtbl[item] = num;
			item++;
        		} else  { 
            		printf("customer Quant Tbl number err\n");
        		}
	}
    }	
  
    fclose(qin);	
    if ( item<63) {
	printf("the number of inter quant tbl's element err\n");	
	return 0;
    }
    return 1;	
}
int fmpeg4_get_intra_quant_tbls(unsigned char *intra_qtbl)
{
    int item=0;
    unsigned char buf;
    unsigned char num;	
    
    sprintf(qin_name,"/ffmpeg/intra_quant.txt");
    qin=fopen(qin_name,"r");
    if( !qin ) {
	printf("open %s fail\n",qin_name);
	return 0;
    }
	
    printf("Use intra quant tbl input name %s\n",qin_name);

    while( !feof(qin) ) {
    	
        fread(&buf, 1, sizeof(unsigned char), qin);

	if ( (buf !=0x20) && (buf >=0x30) && (buf<=0x39) )	{
		num = buf-0x30;
		do {
			fread(&buf, 1, sizeof(unsigned char), qin);
			if ( buf != 0x20)
				num = num*10 + (buf-0x30);
		} while( buf != 0x20 ) ;
        
        		if((item>=0) && (item<64) ){		
			intra_qtbl[item] = num;
			item++;
        		} else  { 
            		printf("customer Quant Tbl number err\n");
        		}
	}
    }	
  
    fclose(qin);	
    if ( item<63) {
	printf("the number of inter quant tbl's element err\n");	
	return 0;
    }
    return 1;	
}
#endif

#define ROIX 0
#define ROIY 0
int enc_idx=0;
int enc_init(AVCodecContext *avctx)
{
    FMpeg4EncContext    *s=(FMpeg4EncContext *)avctx->priv_data;	
    FMP4_ENC_PARAM  enc_param;
    
    if(fmpeg4_enc_fd==0)
	    fmpeg4_enc_fd=open(FMPEG4_ENCODER_DEV,O_RDWR);
    if(fmpeg4_enc_fd<=0) {
	    printf("Fail to open %s\n",FMPEG4_ENCODER_DEV);
	    return -1;
    }    

#ifdef DEBUG_OUT_FILE
    sprintf(dout_name,"/tmp/share/out%d.m4v",getpid());
    dout=fopen(dout_name,"wb");
    printf("Use encoder output name %s\n",dout_name);
#endif

#ifdef DEBUG_IN_FILE
    sprintf(din_name,"/tmp/in%d.yuv",getpid());
    din=fopen(din_name,"wb");
    printf("Use encoder input name %s\n",din_name);
#endif

#ifdef USE_MMAP
#if 0 //test
    enc_mmap_addr=(int)mmap(0,0xa0000,PROT_READ|PROT_WRITE,MAP_SHARED,fmpeg4_enc_fd, 0);
	if(avctx->width==320)
	    enc_mmap_addr+=0x71000;
	printf("mmap_addr=0x%x\n",enc_mmap_addr);
    if(enc_mmap_addr<=0)
        return -1;
#else
    enc_mmap_addr=(int)mmap(0,(avctx->width*avctx->height*3)/2,PROT_READ|PROT_WRITE,MAP_SHARED,fmpeg4_enc_fd, 0);
    if(enc_mmap_addr<=0) {
        close(fmpeg4_enc_fd);
        printf("Error to set FMPEG4_MMAP Fail\n");		
        return -1;
    }
#endif
#endif

    enc_param.u32BitRate=avctx->bit_rate/1000;
    enc_param.u32FrameWidth=avctx->width;
    enc_param.u32FrameHeight=avctx->height;
    enc_param.fFrameRate=(float)avctx->frame_rate/(float)avctx->frame_rate_base;
    enc_param.u32InitialQuant=4;
    enc_param.u32IPInterval=avctx->gop_size;
    enc_param.bEnable4MV=0;
    #ifdef AC_PREDICTION_DISABLE	
    enc_param.ac_disable=0;
    #endif	
    /*
      *   if avctx->fmpeg4_qtbl is equal to
      *   0: select MPEG4 quantization method.
      *   1: select H.263 quantization method and 3gp must use 263 quant
      *   2: select MPEG4 user defined quant tbl "quant.txt" 
      */  
    if(   avctx->fmpeg4_qtbl == 0) {
        enc_param.bH263Quant=0;
     } else if( avctx->fmpeg4_qtbl == 1) {
        enc_param.bH263Quant=1;
     } else if( avctx->fmpeg4_qtbl == 2) {
        enc_param.bH263Quant=0;
        // read quant tables from files
        cust_inter_qtbl = malloc(sizeof(unsigned char)*64);
    	if(!cust_inter_qtbl) {
             fprintf(stdout,"alloc cust_inter_qtbl fail\n");
	    goto 	open_qtbl_file_err;
         }		
         cust_intra_qtbl = malloc(sizeof(unsigned char)*64);
         if(!cust_intra_qtbl) {
	    fprintf(stdout,"alloc cust_intra_qtbl fail\n");
	    goto 	open_qtbl_file_err;
    	}	
         if ( !fmpeg4_get_intra_quant_tbls(cust_intra_qtbl) )
              goto 	open_qtbl_file_err;
         if ( !fmpeg4_get_inter_quant_tbls(cust_inter_qtbl) )
	     goto 	open_qtbl_file_err;	
		
         if ( ioctl(fmpeg4_enc_fd,FMPEG4_IOCTL_SET_INTER_QUANT,cust_inter_qtbl) < 0 ) {
              close(fmpeg4_enc_fd);
	     printf("Error to set FMPEG4_IOCTL_SET_INTER_QUANT\n");
              return -1;
         }

    	if ( ioctl(fmpeg4_enc_fd,FMPEG4_IOCTL_SET_INTRA_QUANT,cust_intra_qtbl) < 0 ) {
             close(fmpeg4_enc_fd);
	    printf("Error to set FMPEG4_IOCTL_SET_INTRA_QUANT\n");
	    return -1;
    	}	
    }
 open_qtbl_file_err:
	 
#if 0 //def H263
    enc_param.bShortHeader=1;
#else
    enc_param.bShortHeader=0;
#endif
    enc_param.bResyncMarker=0;
    enc_param.u32MaxQuant=avctx->qmax;
    enc_param.u32MinQuant=avctx->qmin;
    // ROI related encoding parameters
    enc_param.bROIEnable   = 0;     //0:disable 1:enable
    enc_param.u32ROIX      = ROIX;     // must be a multiple of 16 pixels
    enc_param.u32ROIY      = ROIY;     // must be a multiple of 16 pixels
    s->roi_enable = enc_param.bROIEnable;
    enc_param.u32ROIWidth  = enc_param.u32FrameWidth;   // must be a multiple of 16 pixels
    enc_param.u32ROIHeight = enc_param.u32FrameHeight;  // must be a multiple of 16 pixels
    s->mdinfo = NULL;
    if ( mval.exist==1) {
         int mb_width,mb_height,flag=0; 
         mb_width=(avctx->width+15)/16;
         mb_height=(avctx->height+15)/16;
         s->mdinfo=malloc(sizeof(MACROBLOCK_INFO)*mb_width*mb_height);
         if(s->mdinfo==NULL)    {
              printf("Can't allocate memory for MACROBLOCK_INFO!\n");
	     close(fmpeg4_enc_fd);		  
              return -1;
         }
         s->motion = malloc(sizeof(MOTION_INFO) );
         if(s->motion==NULL)    {
               printf("Can't allocate memory for motion!\n");
	      free( s->mdinfo );		   
	      close(fmpeg4_enc_fd);		   
               return -1;
         }
         motion_info_init(s->motion);
         enc_param.md_interval = mval.md_interval; // tckuo add 2007.01.11
    }
	
    RateControlInit(&ratec,enc_param.u32BitRate*1000,DELAY_FACTOR,RC_AVERAGE_PERIOD,
                  ARG_RC_BUFFER,get_framerate(enc_param.fFrameRate),
                  enc_param.u32MaxQuant,enc_param.u32MinQuant,enc_param.u32InitialQuant);

    if ( ioctl(fmpeg4_enc_fd,FMPEG4_IOCTL_ENCODE_INIT,&enc_param) < 0 )   {
         close(fmpeg4_enc_fd);
	printf("Error to set FMPEG4_IOCTL_ENCODE_INIT\n");
	return -1;
    }
    
    avctx->coded_frame= avcodec_alloc_frame();
    avctx->coded_frame->key_frame=1;

    #ifdef SHOW_PERFORMANCE_EVALUATION_RESULT
    s->pic_count=0;
    time_start(&(s->t1));
    #endif	
	
    return 0;
}

int enc_close(AVCodecContext *avctx)
{
    FMpeg4EncContext    *s=(FMpeg4EncContext *)avctx->priv_data;
	 
    
    if( s->mdinfo)
	free(s->mdinfo);	
    if ( s->motion )
	free(s->motion);
	
    if(fmpeg4_enc_fd)   {
#ifdef USE_MMAP
        munmap(enc_mmap_addr,(avctx->width*avctx->height*3)/2);
#endif
        close(fmpeg4_enc_fd);
    }
    fmpeg4_enc_fd=0;
    av_freep(&avctx->coded_frame);
	
#ifdef DEBUG_OUT_FILE
    fclose(dout);
#endif

#ifdef DEBUG_IN_FILE
    fclose(din);
#endif

#ifdef CUSTOMER_QUANT_TABLE
    if(cust_inter_qtbl)
	free(cust_inter_qtbl);	
    if(cust_intra_qtbl)
	free(cust_intra_qtbl);
#endif
		
#ifdef SHOW_PERFORMANCE_EVALUATION_RESULT	
    s->nsecs = time_stop(&(s->t1)) / 1000;
    printf("spend %lld milliseconds with toal frame count %d = %f(frames/per second)\n", s->nsecs,s->pic_count,(float)((float)s->pic_count)/((float)s->nsecs/(float)1000.0));
    printf("MP4 encoding is done. %d frames are encoded\n",s->pic_count);	
#endif		

    return 0;
}

#define abs(X)    (((X)>0)?(X):-(X))
void motion_dection_1mv(MACROBLOCK_INFO *mb_array, MOTION_INFO   *pEnc_motion, Faraday_ENC_FRAME   *pEnc_frame, unsigned int mbwidth, int mbheight)
{
    	unsigned int x_pos, y_pos;
	unsigned int sad16=0;
    	MACROBLOCK_INFO *mb, *post_mb;
	if ( pEnc_frame->frameindex >= pEnc_motion->md_interval) {
		for(y_pos=0 ; y_pos<mbheight; y_pos++ ) {	
         	     for ( x_pos=0 ; x_pos<mbwidth ; x_pos++ ) {
			mb=mb_array+(y_pos*mbwidth + x_pos);
			post_mb = mb_array+(y_pos*mbwidth + x_pos-1);
			sad16=mb->sad16;
	    		if((x_pos>=pEnc_motion->range_mb_x0_LU) 
					&&(y_pos>=pEnc_motion->range_mb_y0_LU)
					&&(x_pos<pEnc_motion->range_mb_x0_RD)
					&&(y_pos<pEnc_motion->range_mb_y0_RD)) {
				if (((mb->mvs[3].vec.s16x*mb->mvs[3].vec.s16x+mb->mvs[3].vec.s16y*mb->mvs[3].vec.s16y)>pEnc_motion->MV_th0)
					&&(sad16>pEnc_motion->sad_th0)) {
					pEnc_frame->active0++;
				}
			}
			if((x_pos>=pEnc_motion->range_mb_x1_LU)
				&&(y_pos>=pEnc_motion->range_mb_y1_LU)
				&&(x_pos<pEnc_motion->range_mb_x1_RD)
				&&(y_pos<pEnc_motion->range_mb_y1_RD)) {
				if (((mb->mvs[3].vec.s16x*mb->mvs[3].vec.s16x+mb->mvs[3].vec.s16y*mb->mvs[3].vec.s16y)>pEnc_motion->MV_th1)
					&&(sad16>pEnc_motion->sad_th1)){
					pEnc_frame->active1 ++;
				}
			}
			if((x_pos>=pEnc_motion->range_mb_x2_LU)
				&&(y_pos>=pEnc_motion->range_mb_y2_LU)
				&&(x_pos<pEnc_motion->range_mb_x2_RD)
				&&(y_pos<pEnc_motion->range_mb_y2_RD)) {
				if (((mb->mvs[3].vec.s16x*mb->mvs[3].vec.s16x+mb->mvs[3].vec.s16y*mb->mvs[3].vec.s16y)>pEnc_motion->MV_th2)
					&&(sad16>pEnc_motion->sad_th2)){
					pEnc_frame->active2 ++;
				}
			}
		}
    	    }
	}
	
}



int xvid_encode(AVCodecContext *avctx, unsigned char *frame, int buf_size, void * data)
{
    FMpeg4EncContext    *s=(FMpeg4EncContext *)avctx->priv_data;	
    AVFrame             *pict=(AVFrame *)data;
    Faraday_ENC_FRAME   enc_frame;

   if ( s->roi_enable) {
   	enc_frame.roi_X = ROIX;
	enc_frame.roi_Y = ROIY;
   }
   
#ifdef USE_MMAP
    {
        int y_sz,uv_sz;
        y_sz=(avctx->width*avctx->height);
        uv_sz=(avctx->width*avctx->height)/4;
        if(pict->data[0]!=0)       {
            memcpy((char *)enc_mmap_addr,pict->data[0],y_sz);
            memcpy((char *)enc_mmap_addr+y_sz,pict->data[1],uv_sz);
            memcpy((char *)enc_mmap_addr+y_sz+uv_sz,pict->data[2],uv_sz);
            enc_frame.pu8YFrameBaseAddr = enc_mmap_addr;
            enc_frame.pu8UFrameBaseAddr = enc_mmap_addr+y_sz;
            enc_frame.pu8VFrameBaseAddr = enc_mmap_addr+y_sz+uv_sz;
        } else {
            enc_frame.pu8YFrameBaseAddr = 0;
            enc_frame.pu8UFrameBaseAddr = 0;
            enc_frame.pu8VFrameBaseAddr = 0;
        }
    }
#else
    enc_frame.pu8YFrameBaseAddr = (unsigned char *)pict->data[0];   //input user continued virtual address (Y), Y=0 when NVOP
    enc_frame.pu8UFrameBaseAddr = (unsigned char *)pict->data[1];   //input user continued virtual address (U)
    enc_frame.pu8VFrameBaseAddr = (unsigned char *)pict->data[2];   //input user continued virtual address (V)
#endif

#ifdef DEBUG_IN_FILE
    if(pict->data[0]!=0) {
        fwrite((void *)pict->data[0],avctx->width*avctx->height,1,din);
        fwrite((void *)pict->data[1],avctx->width*avctx->height/4,1,din);
        fwrite((void *)pict->data[2],avctx->width*avctx->height/4,1,din);
    }
#endif
   
    enc_frame.bitstream=frame;  //output User Virtual address
    enc_frame.quant = ratec.rtn_quant;
    enc_frame.intra = -1;
     
    if ( ioctl(fmpeg4_enc_fd,FMPEG4_IOCTL_ENCODE_FRAME,&enc_frame) < 0 ) {
	    close(fmpeg4_enc_fd);	
	    printf("Error to set FMPEG4_IOCTL_ENCODE_FRAME\n");
	    return -1;
    }
	
   
    RateControlUpdate(&ratec, enc_frame.quant, enc_frame.length, enc_frame.intra);    
    
    if((mval.exist==1)&&(pict->data[0]!=0))  {  //motion dection enable
         int mb_width,mb_height,flag=0; 
	mb_width=(avctx->width+15)/16;
         mb_height=(avctx->height+15)/16;
     	
         fmpeg4_get_mb_info(s->mdinfo);
	
	//do motion detection
	enc_frame.active0=0;
	enc_frame.active1=0;
	enc_frame.active2=0;
		
	motion_dection_1mv(s->mdinfo, s->motion, &enc_frame, mb_width, mb_height);
	
         //fprintf(stdout,"[%d %d %d %d %d ]\n", enc_frame.frameindex, s->motion->md_interval, enc_frame.active0, enc_frame.active1, enc_frame.active2);
	//fflush(stdout);	 
         motion_alarm_action(&enc_frame,s->motion, avctx->width,avctx->height,pict->data[0],pict->data[1],pict->data[2]);
	
    }
    pict->key_frame = enc_frame.intra; //the return value in enc_frame.intra

#ifdef DEBUG_OUT_FILE
    fwrite((void *)frame,enc_frame.length,1,dout);//fwrite((void *)frame,s->enc_frame.length,1,dout);
#endif

#ifdef SHOW_PERFORMANCE_EVALUATION_RESULT
   s->pic_count++;
#endif	
   
    return enc_frame.length;
}

AVCodec fmpeg4_decoder = {
    "fmpeg4",
    CODEC_TYPE_VIDEO,
    CODEC_ID_MPEG4,
    sizeof(FMpeg4DecContext),
    xvid_decoder_init,
    NULL,					/// encode
    xvid_decoder_end,		/// close
    xvid_decode,			/// decode
    0						/// capabilities
};

AVCodec fmpeg4_encoder = {
    "mpeg4",
    CODEC_TYPE_VIDEO,
    CODEC_ID_MPEG4,
    sizeof(FMpeg4EncContext),
    enc_init,
    xvid_encode,
    enc_close,				/// close
    NULL,					/// decode
	0
};

int RateControlGetQ(void)
{
    return ratec.rtn_quant;
}

void fmpeg4_get_mb_info(MACROBLOCK_INFO *mbinfo)
{
	int err;
	
	err=ioctl(fmpeg4_enc_fd,FMPEG4_IOCTL_ENCODE_INFO,mbinfo);
	if(err<0)
	{
	    printf("Error to set FMPEG4_IOCTL_ENCODE_INFO\n");
	    return;
	}
}

void fmpeg4_get_mb_flag(int *flag)
{
	int err;
	
	err=ioctl(fmpeg4_enc_fd,FMPEG4_IOCTL_ENCODE_FLAG,flag);
	if(err<0)
	{
	    printf("Error to set FMPEG4_IOCTL_ENCODE_FLAG\n");
	    return;
	}
}

